#!/usr/bin/env node

/**
 * MCP HTTP Client - Proper implementation following MCP HTTP Transport specification
 * This client communicates with MCP servers using JSON-RPC 2.0 over HTTP
 */

import axios from 'axios';

class MCPHttpClient {
  constructor(serverUrl = process.env.MCP_SERVER_URL || 'http://localhost:3000/mcp') {
    this.serverUrl = serverUrl;
    this.sessionId = null;
    this.requestId = 1;
    
    // Configure axios instance
    this.client = axios.create({
      timeout: 60000,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json, text/event-stream'
      }
    });
  }

  /**
   * Generate next request ID
   */
  getNextRequestId() {
    return (this.requestId++).toString();
  }

  /**
   * Send JSON-RPC request to MCP server
   */
  async sendRequest(method, params = {}) {
    const request = {
      jsonrpc: '2.0',
      id: this.getNextRequestId(),
      method: method,
      params: params
    };

    const headers = {
      'Content-Type': 'application/json',
      'Accept': 'application/json, text/event-stream'
    };

    // Add session ID if available
    if (this.sessionId) {
      headers['Mcp-Session-Id'] = this.sessionId;
    }

    try {
      const response = await this.client.post(this.serverUrl, request, { headers });
      
      // Validate JSON-RPC response
      if (!response.data.jsonrpc || response.data.jsonrpc !== '2.0') {
        throw new Error('Invalid JSON-RPC response format');
      }

      if (response.data.error) {
        throw new Error(`MCP Error ${response.data.error.code}: ${response.data.error.message}`);
      }

      return response.data.result;
    } catch (error) {
      if (error.response) {
        console.error('HTTP Error:', error.response.status, error.response.statusText);
        console.error('Response:', JSON.stringify(error.response.data, null, 2));
      }
      throw error;
    }
  }

  /**
   * Initialize connection with MCP server
   */
  async initialize() {
    console.log('🔌 Initializing MCP connection...');
    
    const result = await this.sendRequest('initialize', {
      protocolVersion: '2025-03-26',
      capabilities: {
        tools: true,
        resources: false,
        prompts: false
      },
      clientInfo: {
        name: 'mcp-http-client',
        version: '1.0.0'
      }
    });

    this.sessionId = result.sessionId;
    console.log(`✅ MCP connection established`);
    console.log(`   Protocol Version: ${result.protocolVersion}`);
    console.log(`   Server: ${result.serverInfo.name} v${result.serverInfo.version}`);
    if (this.sessionId) {
      console.log(`   Session ID: ${this.sessionId}`);
    }

    return result;
  }

  /**
   * List available tools
   */
  async listTools() {
    return await this.sendRequest('tools/list');
  }

  /**
   * Call a specific tool
   */
  async callTool(name, arguments_) {
    return await this.sendRequest('tools/call', {
      name: name,
      arguments: arguments_
    });
  }

  /**
   * Disconnect from server
   */
  async disconnect() {
    if (this.sessionId) {
      try {
        await this.client.delete(`${this.serverUrl.replace('/mcp', '')}/mcp/session/${this.sessionId}`);
        console.log('🔌 Session terminated');
      } catch (error) {
        console.warn('Warning: Failed to terminate session:', error.message);
      }
      this.sessionId = null;
    }
  }

  // Convenience methods for Java Decompiler tools

  async searchMavenArtifacts(query) {
    return await this.callTool('search_maven_artifacts', { query });
  }

  async downloadArtifact(groupId, artifactId, version, packaging = 'jar') {
    return await this.callTool('download_artifact', {
      groupId,
      artifactId,
      version,
      packaging
    });
  }

  async searchClasses(query, artifactId = null) {
    const args = { query };
    if (artifactId) args.artifactId = artifactId;
    return await this.callTool('search_classes', args);
  }

  async decompileClass(groupId, artifactId, version, className) {
    return await this.callTool('decompile_class', {
      groupId,
      artifactId,
      version,
      className
    });
  }

  async getArtifactVersions(groupId, artifactId) {
    return await this.callTool('get_artifact_versions', {
      groupId,
      artifactId
    });
  }

  async scanLocalProject(projectPath) {
    return await this.callTool('scan_local_project', {
      projectPath
    });
  }

  async searchLocalRepository(query) {
    return await this.callTool('search_local_maven_repository', {
      query
    });
  }

  async findClassInLocalRepository(className, repositoryPath = null) {
    const args = { className };
    if (repositoryPath) args.repositoryPath = repositoryPath;
    return await this.callTool('find_class_in_local_repository', args);
  }
}

/**
 * Example usage of MCP HTTP Client
 */
async function runMCPExample() {
  const client = new MCPHttpClient();

  try {
    console.log('🧪 Testing MCP HTTP Client...\n');

    // Initialize connection
    await client.initialize();
    console.log();

    // List available tools
    console.log('📋 Listing available tools...');
    const tools = await client.listTools();
    console.log(`   Found ${tools.tools.length} tools:`);
    tools.tools.forEach(tool => {
      console.log(`   - ${tool.name}: ${tool.description}`);
    });
    console.log();

    // Search for Maven artifacts
    console.log('🔍 Searching for Maven artifacts (commons-lang3)...');
    const searchResult = await client.searchMavenArtifacts('commons-lang3');
    const searchData = JSON.parse(searchResult.content[0].text);
    console.log(`   Found ${searchData.found} artifacts`);
    
    if (searchData.artifacts.length > 0) {
      const firstArtifact = searchData.artifacts[0];
      console.log(`   First artifact: ${firstArtifact.groupId}:${firstArtifact.artifactId}:${firstArtifact.version}`);
      
      // Download artifact
      console.log('\n📦 Downloading artifact...');
      const downloadResult = await client.downloadArtifact(
        firstArtifact.groupId,
        firstArtifact.artifactId,
        firstArtifact.version
      );
      
      const downloadData = JSON.parse(downloadResult.content[0].text);
      if (downloadData.success) {
        console.log(`   ✅ Downloaded successfully! Found ${downloadData.classCount} classes`);
        
        // Search for classes
        console.log('\n🔎 Searching for classes (String)...');
        const classSearchResult = await client.searchClasses('String');
        const classData = JSON.parse(classSearchResult.content[0].text);
        
        if (classData.totalMatches > 0) {
          console.log(`   Found ${classData.totalMatches} matching classes`);
          const firstClass = classData.matches[0];
          console.log(`   First match: ${firstClass.fullName}`);
          
          // Decompile class
          console.log('\n🛠️  Decompiling class...');
          const decompileResult = await client.decompileClass(
            firstClass.artifact.groupId,
            firstClass.artifact.artifactId,
            firstClass.artifact.version,
            firstClass.fullName
          );
          
          const sourceCode = decompileResult.content[0].text;
          if (sourceCode.includes('Decompilation failed')) {
            console.log('   ⚠️ Decompilation failed');
          } else {
            console.log(`   ✅ Decompilation successful!`);
            console.log(`   📄 Source code length: ${sourceCode.length} characters`);
            console.log('   📝 First 300 characters:');
            console.log(`   ${sourceCode.substring(0, 300)}...`);
          }
        }
      }
    }

    // Get artifact versions
    console.log('\n📌 Getting versions for commons-lang3...');
    const versionResult = await client.getArtifactVersions('org.apache.commons', 'commons-lang3');
    const versionData = JSON.parse(versionResult.content[0].text);
    console.log(`   Found ${versionData.versions.length} versions`);
    console.log(`   Latest versions: ${versionData.versions.slice(0, 5).join(', ')}`);

    // Search local repository
    console.log('\n🏠 Searching local repository...');
    const localResult = await client.searchLocalRepository('commons');
    const localData = JSON.parse(localResult.content[0].text);
    console.log(`   Found ${localData.found} local artifacts`);

    console.log('\n✅ MCP HTTP Client test completed successfully!');

  } catch (error) {
    console.error('\n❌ MCP HTTP Client test failed:', error.message);
    
    if (error.code === 'ECONNREFUSED') {
      console.error('\n💡 Make sure the MCP HTTP server is running:');
      console.error('   npm run build');
      console.error('   npm run start:mcp-http');
    }
  } finally {
    // Clean up
    await client.disconnect();
  }
}

/**
 * Command line interface
 */
async function main() {
  if (process.argv.length > 2) {
    const client = new MCPHttpClient();
    const command = process.argv[2];
    const args = process.argv.slice(3);

    try {
      await client.initialize();

      switch (command) {
        case 'tools':
          const tools = await client.listTools();
          console.log(JSON.stringify(tools, null, 2));
          break;

        case 'search':
          if (args.length === 0) {
            console.error('Usage: node mcp-http-client.js search <query>');
            process.exit(1);
          }
          const result = await client.searchMavenArtifacts(args[0]);
          console.log(result.content[0].text);
          break;

        case 'download':
          if (args.length < 3) {
            console.error('Usage: node mcp-http-client.js download <groupId> <artifactId> <version>');
            process.exit(1);
          }
          const downloadResult = await client.downloadArtifact(args[0], args[1], args[2]);
          console.log(downloadResult.content[0].text);
          break;

        case 'decompile':
          if (args.length < 4) {
            console.error('Usage: node mcp-http-client.js decompile <groupId> <artifactId> <version> <className>');
            process.exit(1);
          }
          const decompileResult = await client.decompileClass(args[0], args[1], args[2], args[3]);
          console.log(decompileResult.content[0].text);
          break;

        case 'example':
          await runMCPExample();
          break;

        default:
          console.log('Available commands:');
          console.log('  tools                                           - List available tools');
          console.log('  search <query>                                  - Search Maven artifacts');
          console.log('  download <groupId> <artifactId> <version>       - Download artifact');
          console.log('  decompile <groupId> <artifactId> <version> <className> - Decompile class');
          console.log('  example                                         - Run example workflow');
      }

    } catch (error) {
      console.error('Error:', error.message);
      process.exit(1);
    } finally {
      await client.disconnect();
    }
  } else {
    // Run example by default
    await runMCPExample();
  }
}

// Run if called directly
if (import.meta.url === `file://${process.argv[1]}`) {
  main().catch(console.error);
}

export { MCPHttpClient };